package cn.exempale.mscapplication.ui;

import android.app.IntentService;
import android.content.Intent;
import android.content.Context;
import android.os.CountDownTimer;
import android.util.Log;
import android.widget.Toast;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import cn.exempale.mscapplication.constant.BaseConstant;
import cn.exempale.mscapplication.dto.PrepareUploadDTO;
import cn.exempale.mscapplication.dto.TaskDTO;
import cn.exempale.mscapplication.dto.UploadDTO;
import cn.exempale.mscapplication.emnus.ProgressType;
import cn.exempale.mscapplication.emnus.StatusType;
import cn.exempale.mscapplication.model.db.dao.ResultDAO;
import cn.exempale.mscapplication.model.db.dao.TaskDAO;
import cn.exempale.mscapplication.model.db.database.AudioDb;
import cn.exempale.mscapplication.model.db.entity.TaskEntity;
import cn.exempale.mscapplication.model.net.callback.Resource;
import cn.exempale.mscapplication.model.repository.FileRepository;
import cn.exempale.mscapplication.model.repository.NormalRepository;
import cn.exempale.mscapplication.utils.JsonUtils;
import cn.exempale.mscapplication.utils.SliceIdGenerator;
import cn.exempale.mscapplication.vo.ProgressStatusVO;
import cn.exempale.mscapplication.vo.ResultVO;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;

/**
 * 语音识别
 */
public class AudioService extends IntentService {

    private static final String ACTION_AUDIO = "cn.exempale.mscapplication.ui.action.audio";
    private static final String EXTRA_FILE_PATH = "cn.exempale.mscapplication.ui.extra.path";

    private String taskId;
    private TaskDAO taskDAO;
    private ResultDAO resultDAO;
    private NormalRepository normalRepository;
    private FileRepository fileRepository;

    private static final String TAG = "AudioService";


    public AudioService() {
        super("AudioService");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        AudioDb audioDb = AudioDb.getDatabase(this);
        taskDAO = audioDb.taskDAO();
        resultDAO = audioDb.resultDAO();
        normalRepository = NormalRepository.newInstance();
        fileRepository = FileRepository.newInstance();
    }

    /**
     * 开启一个任务，如果任务已经在执行，则进入任务队列
     *
     * @see IntentService
     */
    public static void startActionAudio(Context context, String filePath) {
        Intent intent = new Intent(context, AudioService.class);
        intent.setAction(ACTION_AUDIO);
        intent.putExtra(EXTRA_FILE_PATH, filePath);
        context.startService(intent);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            final String action = intent.getAction();
            if (ACTION_AUDIO.equals(action)) {
                final String filePath = intent.getStringExtra(EXTRA_FILE_PATH);
                Log.e(TAG, "onHandleIntent: +++++++++++++++++++++");
                Log.e(TAG, "onHandleIntent: 开始语音转写");
                handleActionAudio(filePath);
            }
        }
    }

    /**
     * 语音转写任务处理
     */
    private void handleActionAudio(String filePath) {
        // 文件定位
        File file = new File(filePath);
        if (!file.exists()) {
            Toast.makeText(this, "文件查找失败", Toast.LENGTH_SHORT).show();
            Log.e(TAG, "handleActionAudio: 文件查找失败:" + filePath);
            // todo 通知
            return;
        }
        // 同步语音识别
        startSyncTask(file);
    }

    /**
     * 同步上传
     *
     * @param file
     */
    private void startSyncTask(File file) {
        // 预上传 dto
        PrepareUploadDTO dto = new PrepareUploadDTO();
        dto.setFile_name(file.getName());
        dto.setFile_len(String.valueOf(file.length()));
        int sliceNum = (int) (file.length() / BaseConstant.SLICE_SIZE);
        if (sliceNum == 0)
            sliceNum = 1;
        dto.setSlice_num(sliceNum);
        // 预上传请求
        Resource resource = normalRepository.prepareUpload(JsonUtils.Obj2Map(dto));
        if (resource.status == StatusType.ERROR) {
            // 预上传失败
            // todo 通知
            Log.e(TAG, "startSyncTask: " + "预上传失败：" + resource.msg);
        } else {
            // 任务id
            taskId = (String) resource.data;
            String msg = "预上传成功taskId:" + taskId;
            // 保存数据库
            TaskEntity taskEntity = new TaskEntity();
            taskEntity.setTaskId(taskId);
            taskEntity.setCrateTime(new Date(Long.parseLong(dto.getTs())));
            taskEntity.setContact("18500000000");
            taskEntity.setFileName(dto.getFile_name());
            taskEntity.setProgressStatus(ProgressType.TASK.getCode());
            // 更新数据库
            taskDAO.save(taskEntity);
            // 更新进度
            Log.e(TAG, "startSyncTask: " + msg);
            // 上传处理
            syncUpload(file);
        }
    }

    /**
     * 同步上传
     *
     * @param file
     */
    private void syncUpload(File file) {
        try {
            // 总片数量
            int times = (int) (file.length() / BaseConstant.SLICE_SIZE) + 1;
            Log.e(TAG, "syncUpload: " + "共" + times + "片");
            FileInputStream fins = new FileInputStream(file);
            SliceIdGenerator sliceIdGenerator = new SliceIdGenerator();
            byte[] slice = new byte[BaseConstant.SLICE_SIZE];
            // 切片上传
            for (int i = 0; i < times; i++) {
                int len = fins.read(slice);
                slice = Arrays.copyOfRange(slice, 0, len);
                UploadDTO uploadDTO = new UploadDTO();
                uploadDTO.setTask_id(taskId);
                uploadDTO.setSlice_id(sliceIdGenerator.getNextSliceId());
                Resource result = fileRepository.upLoad(object2Map(uploadDTO), getSlicePart(slice, file));
                if (result.status == StatusType.ERROR) {
                    // todo 通知
                    Log.e(TAG, "syncUpload: " + "第" + (i + 1) + "片上传失败" + result.msg);
                    updateDb(ProgressType.UPLOAD, false);
                    break;
                } else if (result.status == StatusType.SUCCESS) {
                    Log.e(TAG, "syncUpload: " + "第" + (i + 1) + "片上传成功");
                }
            }
            fins.close();
            updateDb(ProgressType.UPLOAD, true);
            // 合并
            TaskDTO dto = new TaskDTO();
            dto.setTask_id(taskId);
            Resource margeResult = normalRepository.merge(JsonUtils.Obj2Map(dto));
            if (margeResult.status == StatusType.ERROR) {
                // todo 通知
                Log.e(TAG, "syncUpload: 合并失败:" + margeResult.msg);
                updateDb(ProgressType.MARGE, false);
            } else {
                // todo 通知 开启任务
                Log.e(TAG, "syncUpload: " + "合并成功");
                updateDb(ProgressType.MARGE, true);
                // 获取结果
                getProgress();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取处理状态
     */
    private void getProgress() {
        Timer timer = new Timer();
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                TaskDTO dto = new TaskDTO();
                dto.setTask_id(taskId);
                Resource<ProgressStatusVO> resource = normalRepository.getProgress(JsonUtils.Obj2Map(dto));
                if (resource.status == StatusType.SUCCESS) {
                    ProgressStatusVO vo = resource.data;
                    updateDb(ProgressType.match(vo.getStatus()), true);
                    Log.e(TAG, "run: 进度" + vo.getStatus() + vo.getDesc());
                    if (ProgressType.match(vo.getStatus()) == ProgressType.TRANSFER_UPLOAD) {
                        getResult();
                        timer.cancel();
                    }
                } else if (resource.status == StatusType.ERROR) {
                    // todo 任务失败
                    Log.e(TAG, "run: 查询进度失败 " + resource.msg);
                    timer.cancel();
                }
            }
        };
        timer.schedule(timerTask, 0, 1 * 1000 * 60);
    }

    /**
     * 获取处理结果
     */
    private void getResult() {
        TaskDTO dto = new TaskDTO();
        dto.setTask_id(taskId);
        Resource<List<ResultVO>> resource = normalRepository.getResult(JsonUtils.Obj2Map(dto));
        if (resource.status == StatusType.ERROR) {
            // todo 通知 获取结果失败
        } else if (resource.status == StatusType.SUCCESS) {

        }
    }

    /**
     * 更新数据库
     *
     * @param progressType 当前执行节点
     * @param isSuccess    接口执行结果
     */
    public void updateDb(ProgressType progressType, boolean isSuccess) {
        if (!isSuccess)
            // 获取失败状态
            progressType = ProgressType.getFailedType(progressType);
        TaskEntity taskEntity = taskDAO.findByTaskId(taskId);
        taskEntity.setProgressStatus(progressType.getCode());
        taskDAO.update(taskEntity);
    }

    /**
     * 文件上转对象转map
     *
     * @param dto
     * @return
     */
    public Map<String, RequestBody> object2Map(UploadDTO dto) {
        MediaType textType = MediaType.parse("text/plain");
        RequestBody appId = RequestBody.create(textType, dto.getApp_id());
        RequestBody signa = RequestBody.create(textType, dto.getSigna());
        RequestBody ts = RequestBody.create(textType, dto.getTs());
        RequestBody task = RequestBody.create(textType, dto.getTask_id());
        RequestBody sliceId = RequestBody.create(textType, dto.getSlice_id());
        Map<String, RequestBody> map = new HashMap<>();
        map.put("app_id", appId);
        map.put("signa", signa);
        map.put("ts", ts);
        map.put("task_id", task);
        map.put("slice_id", sliceId);
        return map;
    }

    /**
     * 分片数据处理
     *
     * @return
     */
    public MultipartBody.Part getSlicePart(byte[] slice, File file) {
        RequestBody fileBody = RequestBody.create(MediaType.parse("multipart/form-data"), slice);
        MultipartBody.Part body = MultipartBody.Part.
                createFormData("content", file.getName(), fileBody);
        return body;
    }

}